#include <retro.h>
#include "retro.creator.h"
#include "retro.scriptingapi.h"

using namespace Gdiplus;
using namespace Retro::Scripting;
using namespace Retro::Creator::Toolbox;
using namespace SPLib::Geom;

#define ERROR_MEMALLOC  _T("Memory allocation error")

enum ScriptorChild
{
  ScriptorText = 0x1,
  ScriptorToolbar,
};

enum ScriptorItem
{
  ScriptorItemCheckSyntax = 0x1,
  ScriptorItemRun,
};

Scriptor::Scriptor()
{
  API::InitializeAPI(&mManager);
}

Scriptor::~Scriptor()
{
}

VOID    Scriptor::onCreate(Event::WndCreateEvent& iEvent)
{
  // create editor
  {
    mEdit.create(*this, _T("Scintilla"), _T(""),
      WS_CHILD | ES_LEFT | ES_MULTILINE | WS_VSCROLL | ES_WANTRETURN);
    mEdit.setID(ScriptorText);

    API::InitializeEditor(mEdit, Data::ScriptLua);
  }

  // create toolbar
  {
    ToolItem wItems[] = {
      {0,                         _T("Syntax"),       ToolItemLabel},
      {ScriptorItemCheckSyntax,   _T("Check"),        ToolItemButton},
      {0,                         _T(""),             ToolItemSeparator},
      {ScriptorItemRun,           _T("Run Script"),   ToolItemButton},
    };

    mToolbar.create(*this, _T(""),
      0, 0, 0, TOOLBAR_HEIGHT, WS_TABSTOP);
    mToolbar.addItems(wItems, ARRAYSIZE(wItems));
    mToolbar.setBackColor(0xFFFFFF);
    mToolbar.setID(ScriptorToolbar);
  }

  // create layout
  {
    mLayout = new LayoutElement(*this);

    mLayout->padding.setAll(4);
    mLayout->addTop(TOOLBAR_HEIGHT, mToolbar)->margins.bottom = 4;
    mLayout->addFill(mEdit);
  }
}

VOID    Scriptor::onDestroy()
{
  mLayout->destroy();
  delete mLayout;
}

VOID    Scriptor::onSize(Event::SizeEvent& iEvent)
{
  mLayout->doLayout();
}

VOID Scriptor::onNotify(Event::NotifyEvent& iEvent)
{
  switch (iEvent.data->idFrom)
  {
  case ScriptorText:
    if (iEvent.data->code == SCN_MARGINCLICK)
    {
      SCNotification* notify = (SCNotification*)iEvent.data;

      LRESULT line = mEdit.send(
        SCI_LINEFROMPOSITION, notify->position, 0);

      switch (notify->margin)
      {
      case SCRIPT_MARGIN_FOLD:
        mEdit.send(SCI_TOGGLEFOLD, line, 0);
        break;
      }
    }
    break;
  case ScriptorToolbar:
    switch (((ToolBarNotify*)iEvent.data)->itemID)
    {
    case ScriptorItemCheckSyntax:
      checkSyntax();
      break;
    case ScriptorItemRun:
      runScript();
      break;
    }
    break;
  }
}

VOID    Scriptor::onFocus(Event::FocusEvent& iEvent)
{
  if (iEvent.focused)
    mEdit.focus();
}

BOOL    Scriptor::checkSyntax()
{
  LRESULT wLength = mEdit.send(SCI_GETTEXTLENGTH, 0, 0) + 1;
  char* wText     = (char*)malloc(wLength);

  mEdit.send(SCI_GETTEXT, wLength, (LPARAM)wText);

  Scripting::ScriptInterface* wInterface =
    mManager.getInterface(Data::ScriptLua);

  BOOL wResult = wInterface->validate(wText);

  free(wText);

  if (wResult)
  {
    Frame::get()->setStatus(
      FrameScriptor,
      _T("Script syntax valid"));
  }
  else
  {
    Frame::get()->setStatus(
      FrameScriptor,
      wInterface->getErrorMessage());
  }

  return wResult;
}

BOOL    Scriptor::runScript()
{
  LRESULT wLength = mEdit.send(SCI_GETTEXTLENGTH, 0, 0) + 1;
  char*   wText   = (char*)malloc(wLength);
  BOOL    wResult = FALSE;
  PCTSTR  wStatus = _T("");

  mEdit.send(SCI_GETTEXT, wLength, (LPARAM)wText);

  if (*wText == '\0')
  {
    wStatus = _T("Nothing to execute");
  }
  else
  {
    ScriptInterface* wScript = mManager.getInterface(Data::ScriptLua);

    mSkipStatus = FALSE;

    wScript->run(wText);

    switch (wScript->getError())
    {
    case ErrorNone:
      if (!mSkipStatus)
        wStatus = _T("Script executed successfully");

      wResult = TRUE;
      break;
    case ErrorUnknown:
    case ErrorSyntax:
    case ErrorRuntime:
      wStatus = wScript->getErrorMessage();
      break;
    case ErrorMemory:
      wStatus = ERROR_MEMALLOC;
      break;
    }
  }

  Frame::get()->setStatus(FrameScriptor, wStatus);

  mSkipStatus = FALSE;
  free(wText);

  return wResult;
}
